package xyz.lisys.boot.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.val;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import xyz.lisys.boot.pojo.Person;
import xyz.lisys.boot.pojo.ValidUser;
import xyz.lisys.boot.service.EntryService;
import xyz.lisys.boot.service.RuleService;
import xyz.lisys.boot.service.UserService;
import xyz.lisys.boot.client.EntryFeignClient;
import xyz.lisys.boot.util.ResponseUtil;

import javax.annotation.Resource;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Optional;

@Api(value = "首页访问", tags = "首页接口")
@RestController
@Validated
public class IndexController {

    @Resource
    public UserService userService;

    @Resource
    public EntryService entryService;

    @Resource
    public RuleService ruleService;

    @Autowired
    public EntryFeignClient entryFeignClient;

    @Autowired(required = false)
    public RedisTemplate<String, Object> redisTemplate;

    @ApiOperation(value = "展示用户信息", notes = "开发中")
    @GetMapping("/")
    public Object view(@ApiParam(name = "name", value = "用户名", allowEmptyValue = true) String name) {
        Person person = new Person();
        person.setAge(12);
        person.setName(Optional.ofNullable(name).orElse("SystemLight"));
        return ResponseUtil.ok(person);
    }

    @RequiresRoles("user")
    @GetMapping("/user")
    public Object user() {
        return ResponseUtil.ok(userService.findAllUser());
    }

    @GetMapping("/rules")
    public Object rules(@RequestParam(defaultValue = "0") Integer age) {
        val person = new Person();
        person.setAge(age);
        return ResponseUtil.ok(ruleService.executeRule(person));
    }

    @GetMapping("/entry")
    public Object entry() {
        return ResponseUtil.ok(entryService.getEntryByLoadBalancerAnnotation());
    }

    @GetMapping("/entry2")
    public Object entry2() {
        return ResponseUtil.ok(entryFeignClient.getEntry());
    }

    @GetMapping("/user/insert")
    public Object insertUser() {
        userService.insertUser();
        return ResponseUtil.ok("");
    }

    @GetMapping("/validation")
    public Object validation(@Size(min = 3, max = 20) @RequestParam String name) {
        return ResponseUtil.ok(name);
    }

    @PostMapping("/validation")
    public Object validationForPost(@Valid @ModelAttribute ValidUser user, BindingResult result) {
        if (result.hasErrors()) {
            return ResponseUtil.ok("error");
        }
        return ResponseUtil.ok("ok");
    }

    @GetMapping("/redis/get")
    public Object getRedis() {
        if (redisTemplate != null) {
            val now = redisTemplate.opsForValue().get("now");
            return ResponseUtil.ok(now);
        }

        return ResponseUtil.ok("now");
    }

    @GetMapping("/redis/set")
    public Object setRedis() {
        if (redisTemplate != null) {
            redisTemplate.opsForValue().set("now", LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
        }

        return ResponseUtil.ok("ok");
    }

    @GetMapping("/apollo")
    public Object apollo(@Value("${author.name:null}") String name) {
        return ResponseUtil.ok(name);
    }

    @GetMapping("/auth")
    public Object auth(@NotEmpty String username, @NotEmpty String password) {
        Subject subject = SecurityUtils.getSubject();
        subject.login(new UsernamePasswordToken(username, password));
        return ResponseUtil.ok("auth");
    }

    @GetMapping("/auth/fail")
    public Object authenticationFailed() {
        return ResponseUtil.ok("权限验证失败");
    }

    @GetMapping("/auth/unauth")
    public Object unauth() {
        return ResponseUtil.ok("授权失败");
    }

    @GetMapping("/auth/success")
    public Object success() {
        return ResponseUtil.ok("授权成功");
    }

    @ExceptionHandler({
            ConstraintViolationException.class,
            AuthenticationException.class,
            AuthorizationException.class
    })
    public Object exceptionHandler(Exception e) {
        if (e instanceof ConstraintViolationException) {
            return ResponseUtil.msg(200, "ConstraintViolationException", e.getMessage());
        } else if (e instanceof AuthenticationException) {
            return ResponseUtil.msg(200, "AuthenticationException", e.getMessage());
        } else if (e instanceof AuthorizationException) {
            return ResponseUtil.msg(200, "AuthorizationException", e.getMessage());
        }

        return ResponseUtil.ok(e.getMessage());
    }
}
